package com.ruoyi.common.filter;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.PSSParameterSpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.encry.AESUtils;
import com.ruoyi.common.utils.encry.RSAUtils;
import com.ruoyi.common.utils.http.HttpHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class DecryptionFilter implements Filter {
    @Value("${DataMonitorCenter.EncryptionUrl}")
    private String encryptionUrls;
    @Value("${DataMonitorCenter.enabled}")
    private Boolean enctyEnabled;

    private static List<Pattern> convertConfigToPatterns(String config) {
        return Arrays.stream(config.split(","))
                .map(String::trim)
                .map(DecryptionFilter::convertWildcardToRegex)
                .map(Pattern::compile)
                .collect(Collectors.toList());
    }

    private boolean checkUrlInConfig(String requestPath){
        String configPatterns = encryptionUrls;

        // 转换为正则列表
        List<Pattern> patterns = convertConfigToPatterns(configPatterns);

        // 检查是否匹配任意规则
        boolean isMatch = patterns.stream()
                .anyMatch(pattern -> pattern.matcher(requestPath).matches());

        return isMatch;
    }
    private static String convertWildcardToRegex(String wildcard) {
        return "^"
                + wildcard
                .replace(".", "\\.")
                .replace("**", ".*")
//                .replace("*", "[^/]*")
                + "$";
    }



    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
//        chain.doFilter(request, response);
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if(!enctyEnabled || !checkUrlInConfig(httpRequest.getRequestURI())){
            chain.doFilter(request, response);
            return ;
        }
        // 1. 从Header中获取加密的AES密钥和加密数据
        String b64Key = httpRequest.getHeader("X-Encrypted-AES-Key");
        String b64IV = httpRequest.getHeader("X-Encrypted-AES-IV");
        String b64Body = HttpHelper.getBodyString(httpRequest);
        if(StringUtils.isEmpty(b64Key) || StringUtils.isEmpty(b64Body))
        {
            JSONObject result = new JSONObject();
            result.put("code", "400");
            result.put("msg", "请配置加密数据");
            response.getWriter().write(result.toJSONString());
            return ;
        }
        try {
            // 2. RSA解密AES密钥
            byte[] aesKeyBytes = RSAUtils.decryPtion(b64Key);
            // 3. AES解密数据
            String decryptedData = AESUtils.decryPtion(aesKeyBytes, b64Body, b64IV);
            // 4. 将解密后的数据包装为新的Request对象
            ServletRequest decryptedRequest = (ServletRequest) new DecryptedRequestWrapper(httpRequest, decryptedData);

            HttpServletResponse httpResponse = (HttpServletResponse) response;
            // 包装响应对象，拦截原始响应数据
            EncryptedResponseWrapper encryptedResponse = new EncryptedResponseWrapper(httpResponse);
            chain.doFilter(decryptedRequest, encryptedResponse);

            // 对返回的内容进行加密
            try {
                byte[] iv = AESUtils.generateIvTag(12);
                String responseIV = Base64.getEncoder().encodeToString(iv);
                // 加密响应数据
                byte[] encryptedDataResponse = AESUtils.encryPtion(aesKeyBytes, encryptedResponse.getData().getBytes(), responseIV);
                // 设置加密后的响应头
                httpResponse.setHeader("Content-Type", "application/octet-stream");
                httpResponse.setHeader("X-Encrypted-AES-IV", responseIV);

                // 将加密后的数据写入响应
                httpResponse.getOutputStream().write(Base64.getEncoder().encodeToString(encryptedDataResponse).getBytes());
            } catch (Exception e) {
                throw new ServletException("Encryption failed", e);
            }


        } catch (Exception e) {
            JSONObject result = new JSONObject();
            result.put("code", "500");
            result.put("msg", "解密失败");
            response.getWriter().write(result.toJSONString());
            return ;
        }
    }

}

// 自定义Request包装类，替换解密后的数据
class DecryptedRequestWrapper extends HttpServletRequestWrapper {
    private final String decryptedData;

    public DecryptedRequestWrapper(HttpServletRequest request, String decryptedData) {
        super(request);
        this.decryptedData = decryptedData;
    }

    @Override
    public ServletInputStream getInputStream() {
        ByteArrayInputStream bis = new ByteArrayInputStream(decryptedData.getBytes(StandardCharsets.UTF_8));
        return new ServletInputStream() {
            @Override
            public int read() {
                return bis.read();
            }

            @Override
            public boolean isFinished() {
                return bis.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener listener) {
                throw new UnsupportedOperationException();
            }
        };
    }
}

// 自定义响应包装类，拦截原始响应数据
class EncryptedResponseWrapper extends HttpServletResponseWrapper {
    private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    private final javax.servlet.ServletOutputStream servletOutputStream;

    public EncryptedResponseWrapper(HttpServletResponse response) {
        super(response);
        this.servletOutputStream = new ServletOutputStream() {
            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setWriteListener(WriteListener writeListener) {
                throw new UnsupportedOperationException();
            }

            @Override
            public void write(int b) {
                outputStream.write(b);
            }
        };
    }

    @Override
    public javax.servlet.ServletOutputStream getOutputStream() {
        return servletOutputStream;
    }

    public String getData() throws UnsupportedEncodingException {
        return outputStream.toString(StandardCharsets.UTF_8.name());
    }
}